package com.zengge.zbc.utils;

/**
 * 雪花算法工具类
 * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
 * 最高位是符号位，始终为0，不可用 0 = 正数 1 = 负数
 * 41位的时间序列，精确到毫秒级，41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。
 * 10位的机器标识，10位的长度最多支持部署1024个节点。 前5位代表当前机器Id 后5位代表数据中心Id
 * 12位的计数序列号，序列号即一系列的自增id，可以支持同一节点同一毫秒生成多个Id序号，12位的计数序列号支持每个节点每毫秒产生4096个ID序号。
 */
public class SnowFlakeId {


    /**开始时间搓 */
    private final long beginTime = 1583893850000L;

    /**机器工作Id */
    private final long machineIdBits = 5L;

    /**数据中心Id */
    private final long dataCenterIdBits = 5L;

    /**序列号 12位 */
    private final long sequenceBits = 12L;

    /** 机器ID向左移12位 */
    private final long machineIdShift = sequenceBits;

    /** 数据标识id向左移17位(12+5) */
    private final long dataCenterIdShift = sequenceBits + machineIdBits;

    /** 时间截向左移22位(5+5+12) */
    private final long timestampLeftShift = sequenceBits + machineIdBits + dataCenterIdBits;

    /** 工作机器ID(0~31) */
    private long machineId;

    /** 数据中心ID(0~31) */
    private long dataCenterId;

    /** 毫秒内序列(0~4095) */
    private long sequence = 0L;

    /** 上次生成ID的时间截 */
    private long lastTime = -1L;

    /** 生成序列的掩码，这里为4095 (0b111111111111=0xfff=4095) */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);



    /**
     * 对machineId dataCenterId 进行位移得出结果
     * @param machineId machineId
     * @param dataCenterId dataCenterId
     */
    public SnowFlakeId(long machineId, long dataCenterId){
        this.machineId = (machineId<<machineIdShift);
        this.dataCenterId = (dataCenterId<<dataCenterIdShift);
    }

    /**
     * 生成雪花Id
     * @return long
     */
    public synchronized long generateId(){
        long currentTime = System.currentTimeMillis();

        if(currentTime < lastTime){
            throw new RuntimeException("");
        }

        if(currentTime == lastTime){
            sequence = (sequence+1) & sequenceMask;
            if(sequence == 0){
                currentTime = System.currentTimeMillis();
                while (currentTime <= lastTime){
                    currentTime = System.currentTimeMillis();
                }
                return currentTime;
            }
        }else{
            sequence = 0L;
        }
        lastTime = currentTime;
        return ((currentTime - beginTime)<<timestampLeftShift) |machineId |dataCenterId | sequence;
    }

}


















